#include "WindowsFramework.h"
WindowsFramework* WindowsFramework::this_ = NULL; // used in the message handler
const std::wstring& WindowsFramework::GetApplicationDir()
{
	return m_ApplicationDir;
}
void WindowsFramework::VInitialize(U32 width, U32 height, TCHAR* title, RE_RENDERER renderer_type)
{
	VCreateMainWindow(width, height, title);
	VCreateRenderer(renderer_type);
}
void WindowsFramework::VCreateMainWindow(U32 width, U32 height, TCHAR* title)
{
	WNDCLASSEX wc;
	m_hinstance = GetModuleHandle(NULL);
	m_application_name = reinterpret_cast<LPCWSTR>(title);
	wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = m_hinstance;
	wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
	wc.hIconSm = wc.hIcon;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = reinterpret_cast<LPCWSTR>(m_application_name);
	wc.cbSize = sizeof(WNDCLASSEX);

	RegisterClassEx(&wc);

	// Determine the resolution of the clients desktop screen.
	if(width > GetSystemMetrics(SM_CXSCREEN))
	{
		width = GetSystemMetrics(SM_CXSCREEN);
	}
	if(height > GetSystemMetrics(SM_CYSCREEN))
	{
		height = GetSystemMetrics(SM_CYSCREEN);
	}

	m_hwnd = CreateWindow(m_application_name, m_application_name, WS_OVERLAPPEDWINDOW, 15,15, width, height, 0, 0, m_hinstance, 0);


	// Bring the window up on the screen and set it as main focus.
	ShowWindow(m_hwnd, SW_SHOW);
	//SetForegroundWindow(m_hwnd);
	SetFocus(m_hwnd);

	m_width = width; m_height = height;
	// Hide the mouse cursor.
	//ShowCursor(false);
}
void WindowsFramework::VDestroyMainWindow()
{
	// Remove the window.
	DestroyWindow(m_hwnd);
	m_hwnd = NULL;

	// Remove the application instance.
	UnregisterClass(m_application_name, m_hinstance);
	m_hinstance = NULL;

}
IRenderer* WindowsFramework::VCreateRenderer(RE_RENDERER renderer_type)
{
	if(renderer_type == RER_D3D9)
	{
		Renderer9 * renderer = RE_NEW Renderer9();
		if(!renderer->Initialize(m_hwnd))
		{
			assert(0 && "Critical Error! Could not initialize direct3d9!");
			exit(0);
		}
		//renderer is created and ready! Let's inform the engine and return the result
		(m_RandomEngineApp->*m_createdevfunc)();
		//don't forget to invoke the reset function.
		renderer->VResetDeviceState(m_width, m_height);
		(m_RandomEngineApp->*m_resetdevfunc)();
		m_pRenderer = renderer;
		return m_pRenderer;
	}else
	{
		//return RE_NEW Renderer_NULL;
	}
}
void WindowsFramework::VRun()
{
	MSG msg;
	bool done, result;

	U32 current_frame = clock();
	U32 last_frame = current_frame;
	U32 frame_time = 9;
	// Initialize the message structure.
	ZeroMemory(&msg, sizeof(MSG));

	// Loop until there is a quit message from the window or the user.
	done = false;
	while(!done)
	{
		// Handle the windows messages.
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		// If windows signals to end the application then exit out.
		if(msg.message == WM_QUIT)
		{
			done = true;
		}
		else
		{
			//Calculate frame times
			current_frame = clock();
			frame_time = current_frame - last_frame;
			last_frame = current_frame;
			//Update here:
			(m_RandomEngineApp->*m_updatefunc)(frame_time);//1. call update first

			//handle device lose, there is no point in rendering here.
			if(m_bDeviceLost)
			{
				if(m_pRenderer->VRequestState() == RE_RESET)
				{
					m_pRenderer->VResetDeviceState(m_width, m_height);
					(m_RandomEngineApp->*m_resetdevfunc)();
					m_bDeviceLost = false;
				}else
				{
					continue;
				}
			}

			//if we have survived, render and present
			(m_RandomEngineApp->*m_renderfunc)(frame_time);//2. call render
			if(!m_pRenderer->VPresentBackBuffer())
			{
				m_bDeviceLost = true;
				(m_RandomEngineApp->*m_lostdevfunc)();
			}
		}
	}
}
void WindowsFramework::VShutDown()
{
	(m_RandomEngineApp->*m_lostdevfunc)();
	(m_RandomEngineApp->*m_destroydevfunc)();
	//the renderer shoud be destoroyed already by random engine app;
	VDestroyMainWindow();
}

LRESULT CALLBACK WindowsFramework::WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
{
	switch(umessage)
	{
		// Check if the window is being destroyed.
		case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
		}

		// Check if the window is being closed.
		case WM_CLOSE:
		{
			PostQuitMessage(0);		
			return 0;
		}
		default:
		{
			return this_->MessageHandler(hwnd, umessage, wparam, lparam);
		}
	}
}

LRESULT CALLBACK WindowsFramework::MessageHandler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
	switch(umsg)
	{
		// Any other messages send to the default message handler as our application won't make use of them.
		case WM_SIZE:
		{
			(m_RandomEngineApp->*m_lostdevfunc)();
			m_width = LOWORD(lparam);
			m_height = HIWORD(lparam);
			m_pRenderer->VResetDeviceState(LOWORD(lparam), HIWORD(lparam));
			(m_RandomEngineApp->*m_resetdevfunc)();
			break;
		}
		default:
		{
			return DefWindowProc(hwnd, umsg, wparam, lparam);
		}
	}
}



void WindowsFramework::SetRandomEngineObject(RandomEngineApp * app)
{
	m_RandomEngineApp = app;
}
void WindowsFramework::SetUpdateCallback(rndengine_bool_float_func updateclb)
{
	m_updatefunc = updateclb;
}

void WindowsFramework::SetRenderCallback(rndengine_bool_float_func renderclb)
{
	m_renderfunc = renderclb;
}

void WindowsFramework::SetCreateDeviceCallback(rndengine_void_void_func createclb)
{
	m_createdevfunc = createclb;
}
void WindowsFramework::SetDestroyDeviceCallback(rndengine_void_void_func destroyclb)
{
	m_destroydevfunc = destroyclb;
}
void WindowsFramework::SetResetDeviceCallback(rndengine_void_void_func resetclb)
{
	m_resetdevfunc = resetclb;
}
void WindowsFramework::SetLostDeviceCallback(rndengine_void_void_func lostclb)
{
	m_lostdevfunc = lostclb;
}